#include "U2Win32EGLWindow.h"

#include "U2Exception.h"
#include "U2LogManager.h"
#include "U2StringConverter.h"
#include "U2WindowEventUtilities.h"
#include "U2Win32EGLSupport.h"
#include "U2Win32EGLContext.h"


U2EG_NAMESPACE_USING


U2Win32EGLWindow::U2Win32EGLWindow(U2Win32EGLSupport *glsupport)
: U2EGLWindow(glsupport)
{
	mGLSupport = glsupport;
	mNativeDisplay = glsupport->getNativeDisplay();
}

U2Win32EGLWindow::~U2Win32EGLWindow()
{
}

U2EGLContext * U2Win32EGLWindow::createEGLContext() const
{
	return new U2Win32EGLContext(mEglDisplay, mGLSupport, mEglConfig, mEglSurface);
}

void U2Win32EGLWindow::getLeftAndTopFromNativeWindow( int & left, int & top, u2uint width, u2uint height )
{

}

void U2Win32EGLWindow::initNativeCreatedWindow(const NameValuePairList *miscParams)
{

}

void U2Win32EGLWindow::createNativeWindow( int &left, int &top, u2uint &width, u2uint &height, U2String &title )
{
	// destroy current window, if any
	if (mWindow)
		destroy();

#ifdef U2_STATIC_LIB
	HINSTANCE hInst = GetModuleHandle( NULL );
#else
#   if U2_DEBUG_MODE == 1
	HINSTANCE hInst = GetModuleHandle("RenderSystem_GLES_d.dll");
#   else
	HINSTANCE hInst = GetModuleHandle("RenderSystem_GLES.dll");
#   endif
#endif

	mWindow = 0;
	mClosed = false;		
	mColourDepth = mIsFullScreen? 32 : GetDeviceCaps(GetDC(0), BITSPIXEL);
	HWND parent = 0;
	bool vsync = false;
	U2String border;
	bool outerSize = false;
	bool hwGamma = false;
	int monitorIndex = -1;
	HMONITOR hMonitor = NULL;


	if (!mIsExternal)
	{
		DWORD		  dwStyle = WS_VISIBLE | WS_CLIPCHILDREN;
		DWORD		  dwStyleEx = 0;					
		MONITORINFOEX monitorInfoEx;
		RECT		  rc;

		// If we didn't specified the adapter index, or if it didn't find it
		if (hMonitor == NULL)
		{
			POINT windowAnchorPoint;

			// Fill in anchor point.
			windowAnchorPoint.x = left;
			windowAnchorPoint.y = top;


			// Get the nearest monitor to this window.
			hMonitor = MonitorFromPoint(windowAnchorPoint, MONITOR_DEFAULTTONEAREST);
		}

		// Get the target monitor info		
		memset(&monitorInfoEx, 0, sizeof(MONITORINFOEX));
		monitorInfoEx.cbSize = sizeof(MONITORINFOEX);
		GetMonitorInfo(hMonitor, &monitorInfoEx);

		//size_t devNameLen = strlen(monitorInfoEx.szDevice);
		//mDeviceName = new char[devNameLen + 1];

		//strcpy(mDeviceName, monitorInfoEx.szDevice);			


		// No specified top left -> Center the window in the middle of the monitor
		if (left == -1 || top == -1)
		{				
			int screenw = monitorInfoEx.rcMonitor.right  - monitorInfoEx.rcMonitor.left;
			int screenh = monitorInfoEx.rcMonitor.bottom - monitorInfoEx.rcMonitor.top;

			SetRect(&rc, 0, 0, width, height);
			AdjustWindowRect(&rc, dwStyle, false);

			// clamp window dimensions to screen size
			int outerw = (rc.right-rc.left < screenw)? rc.right-rc.left : screenw;
			int outerh = (rc.bottom-rc.top < screenh)? rc.bottom-rc.top : screenh;

			if (left == -1)
				left = monitorInfoEx.rcMonitor.left + (screenw - outerw) / 2;
			else if (monitorIndex != -1)
				left += monitorInfoEx.rcMonitor.left;

			if (top == -1)
				top = monitorInfoEx.rcMonitor.top + (screenh - outerh) / 2;
			else if (monitorIndex != -1)
				top += monitorInfoEx.rcMonitor.top;
		}
		else if (monitorIndex != -1)
		{
			left += monitorInfoEx.rcMonitor.left;
			top += monitorInfoEx.rcMonitor.top;
		}

		mWidth = width;
		mHeight = height;
		mTop = top;
		mLeft = left;

		if (mIsFullScreen)
		{
			dwStyle |= WS_POPUP;
			dwStyleEx |= WS_EX_TOPMOST;
			mTop = monitorInfoEx.rcMonitor.top;
			mLeft = monitorInfoEx.rcMonitor.left;											
		}
		else
		{				
			if (parent)
			{
				dwStyle |= WS_CHILD;
			}
			else
			{
				if (border == "none")
					dwStyle |= WS_POPUP;
				else if (border == "fixed")
					dwStyle |= WS_OVERLAPPED | WS_BORDER | WS_CAPTION |
					WS_SYSMENU | WS_MINIMIZEBOX;
				else
					dwStyle |= WS_OVERLAPPEDWINDOW;
			}

			int screenw = GetSystemMetrics(SM_CXSCREEN);
			int screenh = GetSystemMetrics(SM_CYSCREEN);

			if (!outerSize)
			{
				// Calculate window dimensions required
				// to get the requested client area
				SetRect(&rc, 0, 0, mWidth, mHeight);
				AdjustWindowRect(&rc, dwStyle, false);
				mWidth = rc.right - rc.left;
				mHeight = rc.bottom - rc.top;

				// Clamp window rect to the nearest display monitor.
				if (mLeft < monitorInfoEx.rcMonitor.left)
					mLeft = monitorInfoEx.rcMonitor.left;		

				if (mTop < monitorInfoEx.rcMonitor.top)					
					mTop = monitorInfoEx.rcMonitor.top;					

				if ((int)mWidth > monitorInfoEx.rcMonitor.right - mLeft)					
					mWidth = monitorInfoEx.rcMonitor.right - mLeft;	

				if ((int)mHeight > monitorInfoEx.rcMonitor.bottom - mTop)					
					mHeight = monitorInfoEx.rcMonitor.bottom - mTop;		
			}			
		}

		// register class and create window
		WNDCLASS wc = { CS_OWNDC, U2WindowEventUtilities::_WndProc, 0, 0, hInst,
			LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW),
			(HBRUSH)GetStockObject(BLACK_BRUSH), NULL, "U2GLWindow" };
		RegisterClass(&wc);

		if (mIsFullScreen)
		{
			DEVMODE displayDeviceMode;

			memset(&displayDeviceMode, 0, sizeof(displayDeviceMode));
			displayDeviceMode.dmSize = sizeof(DEVMODE);
			displayDeviceMode.dmBitsPerPel = mColourDepth;
			displayDeviceMode.dmPelsWidth = mWidth;
			displayDeviceMode.dmPelsHeight = mHeight;
			displayDeviceMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
/*
			if (mDisplayFrequency)
			{
				displayDeviceMode.dmDisplayFrequency = mDisplayFrequency;
				displayDeviceMode.dmFields |= DM_DISPLAYFREQUENCY;
				if (ChangeDisplaySettingsEx(mDeviceName, &displayDeviceMode, NULL, CDS_FULLSCREEN | CDS_TEST, NULL) != DISP_CHANGE_SUCCESSFUL)
				{
					U2LogManager::getSingleton().logMessage(LML_NORMAL, "ChangeDisplaySettings with user display frequency failed");
					displayDeviceMode.dmFields ^= DM_DISPLAYFREQUENCY;
				}
			}
			if (ChangeDisplaySettingsEx(mDeviceName, &displayDeviceMode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)								
				U2LogManager::getSingleton().logMessage(LML_CRITICAL, "ChangeDisplaySettings failed");
*/

		}
		// Pass pointer to self as WM_CREATE parameter
		mWindow = CreateWindowEx(dwStyleEx, "U2GLWindow", title.c_str(),
			dwStyle, mLeft, mTop, mWidth, mHeight, parent, 0, hInst, this);

		U2WindowEventUtilities::_addRenderWindow(this);

		U2LogManager::getSingleton().stream()
			<< "Created Win32Window '"
			<< mName << "' : " << mWidth << "x" << mHeight
			<< ", " << mColourDepth << "bpp";
	}

	RECT rc;
	// top and left represent outer window position
	GetWindowRect(mWindow, &rc);
	mTop = rc.top;
	mLeft = rc.left;
	// width and height represent drawable area only
	GetClientRect(mWindow, &rc);
	mWidth = rc.right;
	mHeight = rc.bottom;

	mNativeDisplay = GetDC(mWindow);
	mEglDisplay = eglGetDisplay(mNativeDisplay);
	mGLSupport->setGLDisplay(mEglDisplay);
	mEglSurface = createSurfaceFromWindow(mEglDisplay, mWindow);
}

void U2Win32EGLWindow::reposition( int left, int top )
{

}

void U2Win32EGLWindow::resize( unsigned int width, unsigned int height )
{

}

void U2Win32EGLWindow::windowMovedOrResized()
{

}

void U2Win32EGLWindow::switchFullScreen( bool fullscreen )
{

}

void U2Win32EGLWindow::create(const U2String& name, u2uint width, u2uint height,
                            bool fullScreen, const NameValuePairList *miscParams)
{
    U2String title = name;
    u2uint samples = 0;
    int gamma;
    short frequency = 0;
    bool vsync = false;
    ::EGLContext eglContext = 0;
	int left = 0;
	int top  = 0;

	getLeftAndTopFromNativeWindow(left, top, width, height);

    mIsFullScreen = fullScreen;

    if (miscParams)
    {
        NameValuePairList::const_iterator opt;
        NameValuePairList::const_iterator end = miscParams->end();

        if ((opt = miscParams->find("currentGLContext")) != end &&
            U2StringConverter::parseBool(opt->second))
        {
            eglContext = eglGetCurrentContext();
            if (eglContext)
            {
                U2_EXCEPT(U2Exception::ERR_RENDERINGAPI_ERROR,
                            "currentGLContext was specified with no current GL context",
                            "EGLWindow::create");
            }

            eglContext = eglGetCurrentContext();
            mEglSurface = eglGetCurrentSurface(EGL_DRAW);
        }

        // Note: Some platforms support AA inside ordinary windows
        if ((opt = miscParams->find("FSAA")) != end)
        {
            samples = U2StringConverter::parseUnsignedInt(opt->second);
        }

        if ((opt = miscParams->find("displayFrequency")) != end)
        {
            frequency = (short)U2StringConverter::parseInt(opt->second);
        }

        if ((opt = miscParams->find("vsync")) != end)
        {
            vsync = U2StringConverter::parseBool(opt->second);
        }

        if ((opt = miscParams->find("gamma")) != end)
        {
            gamma = U2StringConverter::parseBool(opt->second);
        }

        if ((opt = miscParams->find("left")) != end)
        {
            left = U2StringConverter::parseInt(opt->second);
        }

        if ((opt = miscParams->find("top")) != end)
        {
            top = U2StringConverter::parseInt(opt->second);
        }

        if ((opt = miscParams->find("title")) != end)
        {
            title = opt->second;
        }

        if ((opt = miscParams->find("externalGLControl")) != end)
        {
            mIsExternalGLControl = U2StringConverter::parseBool(opt->second);
        }
	}

	initNativeCreatedWindow(miscParams);

    if (mEglSurface)
    {
        mEglConfig = mGLSupport->getGLConfigFromDrawable (mEglSurface, &width, &height);
    }

    if (!mEglConfig && eglContext)
    {
        mEglConfig = mGLSupport->getGLConfigFromContext(eglContext);

        if (!mEglConfig)
        {
            // This should never happen.
            U2_EXCEPT(U2Exception::ERR_RENDERINGAPI_ERROR,
                        "Unexpected failure to determine a EGLFBConfig",
                        "EGLWindow::create");
        }
    }

    mIsExternal = (mEglSurface != 0);



    if (!mEglConfig)
    {
        int minAttribs[] = {
            EGL_LEVEL, 0,
            EGL_DEPTH_SIZE, 16,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_NONE
        };

        int maxAttribs[] = {
            EGL_SAMPLES, samples,
            EGL_STENCIL_SIZE, INT_MAX,
            EGL_NONE
        };

        mEglConfig = mGLSupport->selectGLConfig(minAttribs, maxAttribs);
        mHwGamma = false;
    }

    if (!mIsTopLevel)
    {
        mIsFullScreen = false;
        left = top = 0;
    }

    if (mIsFullScreen)
    {
        mGLSupport->switchMode (width, height, frequency);
    }

	if (!mIsExternal)
    {
		createNativeWindow(left, top, width, height, title);
	}

	mContext = createEGLContext();

    ::EGLSurface oldDrawableDraw = eglGetCurrentSurface(EGL_DRAW);
    ::EGLSurface oldDrawableRead = eglGetCurrentSurface(EGL_READ);
    ::EGLContext oldContext  = eglGetCurrentContext();

    int glConfigID;

    mGLSupport->getGLConfigAttrib(mEglConfig, EGL_CONFIG_ID, &glConfigID);
    U2LogManager::getSingleton().logMessage("EGLWindow::create used FBConfigID = " + U2StringConverter::toString(glConfigID));

    mName = name;
    mWidth = width;
    mHeight = height;
    mLeft = left;
    mTop = top;
    mActive = true;
	mVisible = true;

    mClosed = false;
}

